/*
 * Copyright (C) 2009 Show SMS open source project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.bydavy.model.showsms;

//~--- non-JDK imports --------------------------------------------------------

import android.content.ContentUris;
import android.content.Context;

import android.database.Cursor;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import android.net.Uri;

import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

import android.provider.Contacts.People;
import android.provider.Contacts.Phones;

import com.bydavy.android.showsms.R;
import com.bydavy.util.showsms.Log;

/**
 * Class to describe a person
 * A Person have a name, a phone number, a photo and an id (Id in the phone contacts)
 * The person can be in the phone contact or not
 *
 * @version        1.0, 09/06/21
 * @author         Davy L.
 */
public class Person implements Parcelable {
    //~--- static fields ------------------------------------------------------

    /** Key to bundle the id */
    public static final String EXTRAS_PERSON_ID = "EXTRAS_PERSON_ID";

    /** Key to bundle the name */
    public static final String EXTRAS_PERSON_NAME = "EXTRAS_PERSON_NAME";

    /** Key to bundle the phone number */
    public static final String EXTRAS_PERSON_PHONENUMBER = "EXTRAS_PERSON_PHONENUMBER";

    /** Key to bundle the photo */
    public static final String EXTRAS_PERSON_PHOTO = "EXTRAS_PERSON_PHOTO";

    /** ID value if not founded in the phone contacts */
    private static final long NO_ID_FOUNDED = -1;

    /** Creator of a person or persons from a Parcel */
    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    //~--- fields -------------------------------------------------------------

    /** person photo */
    private Bitmap photo = null;

    /** id of person in the phone contacts if exist, or NO_ID_FOUNDED value */
    private long id = NO_ID_FOUNDED;

    /** person name */
    private String name;

    /** person phone number */
    private String phoneNumber;

    //~--- constructors -------------------------------------------------------

    /**
     * Construct a <Person> from a parcel
     *
     * @param in parcel containing a <Person>
     */
    public Person(Parcel in) {
        id          = in.readLong();
        name        = in.readString();
        phoneNumber = in.readString();

        boolean photo_exist = (Boolean) in.readValue(boolean.class.getClassLoader());

        if (photo_exist == true) {
            photo = in.readParcelable(Bitmap.class.getClassLoader());
        }
    }

    /**
     * Constructs person from her phone number
     * Alll informations like photo, name and id while by exctract from the phone contacts if exist
     *
     * @param context context application
     * @param _phoneNumber phone number of the person (can't be null)
     */
    public Person(String _phoneNumber, Context context) {
        phoneNumber = _phoneNumber;
        id          = Person.findId(context, phoneNumber);

        if (id == NO_ID_FOUNDED) {
            name  = null;
            photo = null;
        } else {
            name  = Person.findName(context, id);
            photo = Person.findPhoto(context, id);
        }
    }

    /**
     * Constructs a person from attributes
     * All attributes must be defined
     * @param _id id in the phone contacts
     * @param _name name of the person
     * @param _phoneNumber phone number of the person
     * @param _photo photo of the person
     */
    public Person(long _id, String _name, String _phoneNumber, Bitmap _photo) {
        id          = _id;
        name        = _name;
        phoneNumber = _phoneNumber;
        photo       = _photo;
    }

    //~--- get methods --------------------------------------------------------

    /**
     * Getter : the Name if exist or phoneNumber
     *
     * @return Name if exist or the phone number
     */
    public String getNameOrPhoneNumber() {
        if (name != null) {
            return name;
        } else {
            return phoneNumber;
        }
    }

    //~--- methods ------------------------------------------------------------

    /**
     * Constructs a person from a bundle
     *
     * @param bundle bundle containing a <Person>
     *
     * @return <person> or Null if no person bundled
     */
    static public Person constructFromBundle(Bundle bundle) {
        long id = bundle.getLong(EXTRAS_PERSON_ID);

        // No person object bundled
        if (id == 0L) {
            return null;
        }

        String name        = bundle.getString(EXTRAS_PERSON_NAME);
        String phoneNumber = bundle.getString(EXTRAS_PERSON_PHONENUMBER);
        Bitmap photo       = bundle.getParcelable(EXTRAS_PERSON_PHOTO);

        return new Person(id, name, phoneNumber, photo);
    }

    /**
     * Bundle a person object
     *
     * @return <Person> bundled
     */
    public Bundle toBundle() {
        return toBundle(new Bundle());
    }

    /**
     *   Add <person> bundle to an existing bundle
     *   /!\ One bundle object can't contains more that one <Person>
     *
     *   @param b existing bundle
     *   @return <Person> bundled
     */
    public Bundle toBundle(Bundle b) {
        b.putLong(EXTRAS_PERSON_ID, id);
        b.putString(EXTRAS_PERSON_NAME, name);
        b.putString(EXTRAS_PERSON_PHONENUMBER, phoneNumber);
        b.putParcelable(EXTRAS_PERSON_PHOTO, photo);

        return b;
    }

    /**
     * test if the Person exist in the phone contacts
     *
     * @return true if the person exist in the phone contacts, in the others case false
     */
    public boolean existInPhoneContacts() {
        if (id == NO_ID_FOUNDED) {
            return false;
        } else {
            return true;
        }
    }

    //~--- get methods --------------------------------------------------------

    /**
     * Getter : the person photo retrieved from the phone contact
     * /!\ Can by call even if the contact don't have a photo or isn't in the phone contacts, in these two cases a particular photo will be returned
     *
     * @param context
     * @return the person photo (96 x 96px)
     */
    public Bitmap getPhoto(Context context) {

        // Unknown person
        if (!existInPhoneContacts()) {
            Log.v("This person is unknow");

            return BitmapFactory.decodeResource(context.getResources(), R.drawable.no_photo);
        }

        // Knowned person
        else {
            if (!hasPhoto()) {
                Log.v("This person is know but don't have photo");

                return BitmapFactory.decodeResource(context.getResources(), R.drawable.no_photo);
            }
        }

        Log.v("This person have a photo");

        return photo;
    }

    /**
     * Get the person id (id) in the phone contacts
     * If the person doesn't exist in the phone contacts return Person.NO_ID_FOUNDED
     * @return person id or Person.NO_ID_FOUNDED if the person exist or not in the phone contacts
     */
    public long getId() {
        return id;
    }

    /**
     * Get the person name retrieved from the phone contact
     *
     * @param context application context
     * @return person name if exist in the phone contacts or a default string (person_unknow_name) definied in string.xml
     */
    public String getName(Context context) {
        if (name == null) {
            return context.getString(R.string.person_unknow_name);
        } else {
            return name;
        }
    }

    /**
     * Getter : the person phone number
     *
     * @return person phone number
     */
    public String getPhoneNumber() {
        return phoneNumber;
    }

    //~--- methods ------------------------------------------------------------

    /**
     * Find person id from phoneNumber in the phone contacts
     *
     * @param context application context
     * @param phoneNumber person phone number
     *
     * @return person id if person founded in the phone contacts otherwise Person.NO_ID_FOUNDED
     */
    private static long findId(Context context, String phoneNumber) {

        // Get the base URI
        Uri myPerson = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, Uri.encode(phoneNumber));

        // Column to return
        String[] projection = new String[] { Phones.PERSON_ID };
        Cursor   cursor     = context.getContentResolver().query(myPerson, projection, null, null, null);

        if (cursor.moveToFirst()) {
            int  idColumn = cursor.getColumnIndex(Phones.PERSON_ID);
            Long id       = cursor.getLong(idColumn);

            Log.v("Found id person: " + id);
            cursor.close();

            return id;
        }

        cursor.close();
        Log.v("Not Found id person");

        return NO_ID_FOUNDED;
    }

    /**
     * Find person name from person id in the phone contacts
     *
     * @param context application context
     * @param id person id
     *
     * @return person name if person id founded in the phone contacts otherwise return null
     */
    private static String findName(Context context, Long id) {

        // Get the base URI
        Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, id);

        // Column to return
        String[] projection = new String[] { People.NAME };
        Cursor   cursor     = context.getContentResolver().query(myPerson, projection, null, null, null);

        if (cursor != null) {
            try {
                if (cursor.getCount() > 0) {
                    cursor.moveToFirst();

                    int    nameColumn = cursor.getColumnIndex(People.NAME);
                    String name       = cursor.getString(nameColumn);

                    Log.v("Found name person: " + name);
                    cursor.close();

                    return name;
                }
            } finally {
                cursor.close();
            }
        }

        Log.v("Not Found name person");

        return null;
    }

    /**
     * Find person photo from person id in the phone contacts
     *
     *
     * @param context application context
     * @param id person id in the phone contacts
     *
     * @return person photo if person id founded in the phone contacts otherwise return null
     */
    private static Bitmap findPhoto(Context context, Long id) {
        Uri uri = Uri.parse("content://contacts/people/" + id);

        return People.loadContactPhoto(context, uri, 0, null);
    }

    //~--- get methods --------------------------------------------------------

    /**
     * test if the person has a photo in the phone contacts
     * /!\ Not all persons in the phone contacts have a photo
     * @return true if the person has photo, otherwise return false
     */
    public boolean hasPhoto() {
        if (photo == null) {
            return false;
        }

        return true;
    }

    //~--- methods ------------------------------------------------------------

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public int describeContents() {

        // TODO Auto-generated method stub
        return 0;
    }

    /**
     * Save the <Person> in a parcel
     *
     * @param dest parcel'll contains the <person>
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(id);
        dest.writeString(name);
        dest.writeString(phoneNumber);

        if (hasPhoto()) {
            dest.writeValue(true);
            dest.writeParcelable(photo, Bitmap.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            dest.writeValue(false);
        }
    }

    /**
     * All person informations in a String (phone number, id and name)
     *
     * With this Layout:
     * Person phone number : XXXX \n
     * Person id :  XXXX \n
     * Person name : XXXX \n
     * @return
     */
    @Override
    public String toString() {
        return "Person phone number : " + phoneNumber + "\n" + "Person id : " + id + "\n" + "Person name : " + name;
    }
}
